package pkg

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"time"
	"k8s.io/klog"
)

const SOCKET_PATH_PREFIX ="/tmp/click-daemon"
const TempEnv = "/tmp/env"
const ENV = "LINX_SERVER_PATH"
const TIMEOUT = 3

type Server struct {
	listenerPath string
	stepsFile string
	resultDir string
	imagesWinePath string
	imagesWindowsPath string
	imageButton imageButton
	validator validator
	resFile string
	succeed int
	failed int
	appRunTime map[string]time.Time
	maxAppLiftTime time.Duration
}

func NewServer(stepsFile,resultDir string,imagesPath string,x,y,width,height int,maxAppLiftTime time.Duration)(*Server,error){
	s:=&Server{
		validator: newAttrValidator(),
		stepsFile: stepsFile,
		resultDir: resultDir,
		imagesWinePath: fmt.Sprintf("%s/wine",imagesPath),
		imagesWindowsPath: fmt.Sprintf("%s/windows",imagesPath),
		imageButton: imageButton{
			x:x,
			y:y,
			width: width,
			height: height,
		},
		resFile: fmt.Sprintf("%s/result.txt",resultDir),
		appRunTime: map[string]time.Time{},
		maxAppLiftTime:maxAppLiftTime,
	}

	err:=s.init()
	if err!=nil{
		return nil,err
	}
	return s,nil
}

func (s *Server)init()error{
	found,err:=pathExists(SOCKET_PATH_PREFIX)
	if err!=nil{
		klog.Errorf("judge path exists error:%v\n",err)
		return err
	}

	if !found{
		err:=os.Mkdir(SOCKET_PATH_PREFIX,os.ModePerm)
		if err!=nil{
			klog.Errorf("mkdir %s error:%v\n",SOCKET_PATH_PREFIX,err)
			return err
		}else{
			klog.Infof("mkdir %s success\n",SOCKET_PATH_PREFIX)
		}
	}else{
		klog.Infof("Dir %s exists\n",SOCKET_PATH_PREFIX)
	}

	t:=time.Now()
	y,m,d:=t.Date()
	h,mm,ss:=t.Clock()
	s.listenerPath=fmt.Sprintf("%s-%d-%d-%d-%d-%d-%d",SOCKET_PATH_PREFIX,y,m,d,h,mm,ss)
	//err=ioutil.WriteFile(s.listenerPath,nil,os.ModePerm)
	//if err!=nil{
	//	klog.Errorf("new file %s error:%v\n",s.listenerPath,err)
	//	return err
	//}

	cmd:=fmt.Sprintf("export %s=%s",ENV,s.listenerPath)
	klog.Infof("client run %s\n",cmd)
	err=ioutil.WriteFile(TempEnv,[]byte(cmd),os.ModePerm)
	if err!=nil{
		klog.Errorf("write %s to %s error:%v\n",cmd,TempEnv,err)
		return err
	}


	return nil

}

func (s *Server)Run(ctx context.Context)error{
	l,err:=net.Listen("unix",s.listenerPath)
        klog.Info("listen...\n")
	if err!=nil{
		klog.Errorf("new net.Listen with error:%v\n",err)
		return err
	}

	go s.conTask()
	for {
		fd, err := l.Accept()
                klog.Info("accept...\n")
		if err != nil {
			klog.Errorf("accept error:%v\n", err)
			return err
		}
		go s.run(fd)
	}
}


func (s *Server)run(c net.Conn) {
	buf := make([]byte, 10240)
	nr, err := c.Read(buf)
	if err != nil {
		return
	}

	data := buf[0:nr]
	klog.Infof("Server got:%v\n", string(data))
	//_, err = c.Write([]byte("got wine prefix"))
	_, err = c.Write([]byte("got wine prefix"))
	if err != nil {
		klog.Errorf("Write error: %v\n", err)
		return
	}

	buf = make([]byte, 10240)
	nr, err = c.Read(buf)
	if err != nil {
		return
	}
	data = buf[0:nr]
	klog.Infof("Server got:%v\n", string(data))
	klog.Infof("Server got:%v\n", getEndIndex(data))
	data=data[:getEndIndex(data)+1]
	strData:=fmt.Sprintf("%s}",data)
	klog.Infof("Server got:%v\n", string(strData))
	app:=MegApp{}
	if err:=json.Unmarshal([]byte(strData),&app);err!=nil{
		klog.Errorf("Unmarshal to MegApp error:%v",err)
		return
	}
	appName:=app.Body.ProgramName
	klog.Infof("Server got:%v\n", appName)
	appName=appName[:len(appName)-4]
	s.appRunTime[appName]=time.Now()



	stepFile:=fmt.Sprintf("%s/%s",s.stepsFile,appName)
	imagesWinePath:=fmt.Sprintf("%s/%s",s.imagesWinePath,appName)
	imagesWindowsPath:=fmt.Sprintf("%s/%s",s.imagesWindowsPath,appName)
	appResDir:=fmt.Sprintf("%s/%s",s.resultDir,appName)
	mkdirIfNotFound(imagesWinePath)
	mkdirIfNotFound(imagesWindowsPath)
	mkdirIfNotFound(appResDir)
        mkdirIfNotFound(s.stepsFile)
	e,err:=newExecutor(stepFile,imagesWinePath,c,s.imageButton)
	if err!=nil{
		klog.Errorf("new executor error:%v",err)
		return
	}
	resultFile:=fmt.Sprintf("%s/%s.txt",appResDir,"result")
	resultData:=""
	lastButtons:=[]Button{}
	for {
		if e.isFinished(){
			klog.Infof("run test file success,success button num %d,failed button num %d",e.successNum,e.failedNum)
			klog.Infof("result file is:%s\n",resultFile)
			err:=ioutil.WriteFile(resultFile,[]byte(resultData),os.ModePerm)
			if err != nil {
				klog.Errorf("write fail error:%v",err)
			}
			msg:=fmt.Sprintf("succeed:%d\nfailed:%d",s.succeed,s.failed)
			ioutil.WriteFile(s.resFile,[]byte(msg),os.ModePerm)
			return
		}
		buttons,err:=e.get_buttons()
		if err!=nil{
			klog.Errorf("get button error:%v",err)
			return
		}

		newButtons:=buttons[len(lastButtons):]
		lastButtons=buttons
		wineImage:=fmt.Sprintf("%s/%d.png",imagesWinePath,e.pi)
		windowsImage:=fmt.Sprintf("%s/%d.png",imagesWindowsPath, e.pi)
		if !s.validator.validate(newButtons,[]Button{e.snapshots[e.pi]},wineImage,windowsImage){
			klog.Errorf("valid failed,expected %v but %v",[]Button{e.snapshots[e.pi]},newButtons)
			resultData=fmt.Sprintf("%s\nvalidate button %s failed",resultData,newButtons[0].Name)
			e.failedNum++
			s.failed++
			copy(windowsImage,fmt.Sprintf("%s/windows_%02d.png",appResDir,e.pi))
			copy(wineImage,fmt.Sprintf("%s/wine_%02d.png",appResDir,e.pi))
		}else{
			klog.Infof("validate button %s success",newButtons);
			resultData=fmt.Sprintf("%s\nvalidate button %s success",resultData,newButtons[0].Name)
			e.successNum++
                        s.succeed++
		}
		err,exit:=e.exec()
		if err!=nil{
			klog.Errorf("exec error:%v",err)
			return
		}
		if exit{
			klog.Infof("run step file %s success",s.stepsFile)
			return
		}

	}
}


func getEndIndex(str []byte) int{
	for i:=range str{
		if str[i]=='\n'{
			return i
		}
	}
	return -1
}


func (s *Server)conTask(){
	time.Sleep(5*time.Minute)
	n:=time.Now()
	for name,t:=range s.appRunTime{
		if n.Sub(t)>s.maxAppLiftTime{
			killAPP(name)
		}
	}
}
